﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
using USClientKernel.Template;

namespace Common.Utils
{
    public class AssetInfo
    {
        private Object m_object;
        private bool m_bIsLoading;
        public Type AssetType { get; set; }
        public string Path { get; set; }
        public int RefCount { get; set; }

        // 是否有效 
        public bool IsValid
        {
            get { return null != m_object; }
        }

        // 是否正在加载
        public bool IsLoading
        {
            get { return m_bIsLoading; }
        }

        // 是否加载完成
        public bool IsDone
        {
            get { return (null != m_object && !m_bIsLoading); }
        }

        // 同步加载
        public Object AssetObject
        {
            get
            {
                if (!IsValid && !IsLoading)
                    _ResourcesLoad();
                return m_object;
            }
        }

        /// <summary>
        /// 协程加载
        /// </summary>
        /// <param name="_loaded">完成回调函数</param>
        public IEnumerator GetCoroutineObject(Action<Object> _loaded)
        {
            if (!IsValid && !IsLoading)
            {
                _ResourcesLoad();
                yield return null;
            }
            if (null != _loaded) _loaded(m_object);
            yield return null;
        }

        /// <summary>
        /// 资源加载
        /// </summary>
        private Object _ResourcesLoad()
        {
            try
            {
                m_bIsLoading = true;
                // 尝试从AssetBundle里加载
                m_object = AssetsBundleManager.Instance.LoadAssetSync(Path);
                // 如果没加载到去Resources里找
                if (null == m_object)
                    m_object = Resources.Load(Path);
                m_bIsLoading = false;
                Debug.Log("Resources Load Asset Path :" + Path);
            }
            catch (Exception e)
            {
                m_bIsLoading = false;
                Debug.Log(e.ToString());
            }
            return null;
        }


        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <param name="_loaded">完成回调函数</param>
        public IEnumerator GetAsyncObject(Action<Object> _loaded)
        {
            return GetAsyncObject(_loaded, null);
        }

        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <param name="_loaded">完成回调函数</param>
        /// <param name="_progress">进度回调函数</param>
        public IEnumerator GetAsyncObject(Action<Object> _loaded, Action<float> _progress)
        {
            //have loaded
            if (null != m_object)
            {
                _loaded(m_object);
                yield break;
            }

            // try load Object from AssetsBundleManager
            m_bIsLoading = true;
            bool bLoadFromAssetBundleDone = false;
            try
            {
                AssetsBundleManager.Instance.LoadAssetAsync(
                    Path,
                    (Object o) =>
                    {
                        m_object = o;
                        bLoadFromAssetBundleDone = true;
                    },
                    (float progress) =>
                    {
                        if (null != _progress)
                            _progress(progress);
                    }
                );
            }
            catch (Exception e)
            {
                Debug.Log(e.ToString());
            }

            yield return new WaitUntil(() => bLoadFromAssetBundleDone);

            if (null != m_object)
            {
                _loaded(m_object);
                m_bIsLoading = false;
                yield break;
            }

            // 继续在Resources中找
            ResourceRequest _resRequest = Resources.LoadAsync(Path);

            while (!_resRequest.isDone)
            {
                if (_resRequest.progress >= 0.9f)
                    _resRequest.allowSceneActivation = true;
                if (null != _progress)
                    _progress(_resRequest.progress);
                yield return null;
            }

            if (null != _progress)
                _progress(_resRequest.progress);

            m_object = _resRequest.asset;
            m_bIsLoading = false;
            Debug.Log("Resources Async Load Asset Path :" + Path);

            if (null != _loaded)
                _loaded(m_object);

            yield return _resRequest;
        }
    }

    public class ResManager : Singleton<ResManager>
    {
        private Dictionary<string, AssetInfo> _dicAssetInfo;

        protected override void Init()
        {
            _dicAssetInfo = new Dictionary<string, AssetInfo>();
        }


        #region GetAssetInfo & Instantiate Object

        private AssetInfo GetAssetInfo(string _path)
        {
            return GetAssetInfo(_path, null);
        }

        private AssetInfo GetAssetInfo(string _path, Action<Object> _loaded)
        {
            if (string.IsNullOrEmpty(_path))
            {
                Debug.LogError("[GetAssetInfo Error]null _path name!!!");
                if (null != _loaded)
                    _loaded(null);
            }

            AssetInfo _assetInfo = null;

            if (!_dicAssetInfo.TryGetValue(_path, out _assetInfo))
            {
                _assetInfo = new AssetInfo();
                _assetInfo.Path = _path;
                _dicAssetInfo.Add(_path, _assetInfo);
            }

            _assetInfo.RefCount++;
            return _assetInfo;
        }

        private T Instantiate<T>(T _obj) where T : Object
        {
            return Instantiate<T>(_obj, null);
        }

        private T Instantiate<T>(T _obj, Action<T> _loaded) where T : Object
        {
            if (null == _obj)
            {
                Debug.LogError("Error: null Instantiate _obj");
                return null;
            }

            T _retObj = Object.Instantiate<T>(_obj);
            if (null == _retObj)
            {
                Debug.LogError("Error: null Instantiate _retObj");
                return null;
            }

            if (null != _loaded)
            {
                _loaded(_retObj);
            }

            return _retObj;
        }

        #endregion


        #region Load Rescources

        public Object Load(string _path)
        {
            AssetInfo assetInfo = GetAssetInfo(_path);
            if (null != assetInfo)
                return assetInfo.AssetObject;
            return null;
        }

        public Object Load(string _path, out AssetInfo _outAssetInfo)
        {
            _outAssetInfo = GetAssetInfo(_path);
            if (null != _outAssetInfo)
            {
                return _outAssetInfo.AssetObject;
            }
            return null;
        }

        #endregion


        #region Load Coroutine Rescources

        public void LoadCoroutine(string _path, Action<Object> _loaded)
        {
            AssetInfo assetInfo = GetAssetInfo(_path, _loaded);
            if (null != assetInfo)
                CoroutineController.Instance.StartCoroutine(assetInfo.GetCoroutineObject(_loaded));
        }

        public void LoadCoroutine(string _path, Action<Object> _loaded, out AssetInfo _outAssetInfo)
        {
            _outAssetInfo = GetAssetInfo(_path, _loaded);
            if (null != _outAssetInfo)
                CoroutineController.Instance.StartCoroutine(_outAssetInfo.GetCoroutineObject(_loaded));
        }

        #endregion


        #region Load Async Rescources

        public void LoadAsync(string _path, Action<Object> _loaded)
        {
            LoadAsync(_path, _loaded, null);
        }

        public void LoadAsync(string _path, Action<Object> _loaded, Action<float> _progress)
        {
            AssetInfo assetInfo = GetAssetInfo(_path, _loaded);
            if (null != assetInfo)
                CoroutineController.Instance.StartCoroutine(assetInfo.GetAsyncObject(_loaded, _progress));
        }

        public void LoadAsync(string _path, Action<Object> _loaded, Action<float> _progress, out AssetInfo _outAssetInfo)
        {
            _outAssetInfo = GetAssetInfo(_path, _loaded);
            if (null != _outAssetInfo)
                CoroutineController.Instance.StartCoroutine(_outAssetInfo.GetAsyncObject(_loaded, _progress));
        }

        #endregion


        #region Load Rescources & Instantiate Object

        public T LoadInstantiate<T>(string _path) where T : Object
        {
            T _obj = (T)Load(_path);
            return Instantiate<T>(_obj);
        }

        public void LoadCoroutineInstantiate<T>(string _path, Action<T> _loaded) where T : Object
        {   
            LoadCoroutine(_path, _obj => { Instantiate<T>((T)_obj, _loaded); });
        }

        public void LoadAsyncInstantiate<T>(string _path, Action<T> _loaded) where T : Object
        {
            LoadAsync(_path, _obj => { Instantiate<T>((T)_obj, _loaded); });
        }

        public void LoadAsyncInstantiate<T>(string _path, Action<T> _loaded, Action<float> _progress) where T : Object
        {
            LoadAsync(_path, _obj => { Instantiate<T>((T)_obj, _loaded); }, _progress);
        }

        #endregion
    }
}
